
// import fs from "node:fs/promises";
// import path from "node:path";
// import * as roar from './vm.mjs'
import * as roar from '../../jsruntime/runtime/vm.mjs'

let path = {
    join(...paths) {
        let p = paths
            .map(part => part.replace(/\/+$/, ''))  // 移除末尾的斜杠
            .filter(part => part)                   // 过滤掉空的部分
            .join('/')                              // 用 '/' 连接
            .replace(/\/{2,}/g, '/');               // 替换多个连续斜杠为一个
        return p;
    },
    relative(from, to) {
        const fromParts = from.split('/').filter(Boolean);  // 去掉空的部分
        const toParts = to.split('/').filter(Boolean);

        let commonLength = 0;
        for (let i = 0; i < Math.min(fromParts.length, toParts.length); i++) {
            if (fromParts[i] !== toParts[i]) break;
            commonLength++;
        }

        const upSteps = fromParts.length - commonLength;  // 需要返回的上级目录数量
        const remainingPath = toParts.slice(commonLength).join('/');  // 剩下的目标路径

        let p = '../'.repeat(upSteps) + remainingPath;
        return p;
    }
}
function utf8EncodeToIntArray(str) {
    const encoder = new TextEncoder();        // 创建 TextEncoder 实例
    const uint8Array = encoder.encode(str);   // 编码字符串为 UTF-8 Uint8Array
    return Array.from(uint8Array);            // 将 Uint8Array 转换为普通整数数组
}
function utf8DecodeFromIntArray(arr) {
    const uint8Array = new Uint8Array(arr);   // 将整数数组转换为 Uint8Array
    const decoder = new TextDecoder();        // 创建 TextDecoder 实例
    return decoder.decode(uint8Array);        // 解码为 UTF-8 字符串
}
class FS {
    SINGLE_FILE_SIZE_LIMIT = 1048576;
    TOTAL_FILE_SIZE_LIMIT = 1048576;
    files = new Map();
    async readFile(path, options) {
        let intarr = this.files.get(path);
        if (!intarr) {
            intarr = [];
        } else {
            intarr = [...intarr];
        }
        if (options?.encoding == 'utf8') {
            let content = utf8DecodeFromIntArray(intarr);
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    resolve(content);
                }, 1);
            });
        } else {
            let i_content = [...intarr];
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    resolve(i_content);
                }, 1);
            });
        }
    }
    async writeFile(path, content, options) {
        if (options?.encoding === 'utf8') {
            content = utf8EncodeToIntArray(content);
        } else {
            content = content.map(v => v & 0xff);
        }
        this.files.set(path, content);
    }
    async appendFile(path, content, options) {
        if (options?.encoding === 'utf8') {
            content = utf8EncodeToIntArray(content);
        } else {
            content = content.map(v => v & 0xff);
        }
        let oldContent = this.files.get(path);
        if (oldContent) {
            content = oldContent.concat(content);
        } else {
            content = [...content];
        }
        this.files.set(path, content);

    }
    async unlink(path) {
        this.files.delete(path);
    }
}

class VRRFile {
    basePath = '';
    receiveCallback;
    fs;
    constructor(key, receiveCallback) {
        this.basePath = path.join('./local/', key.replace('-', '/'), 'files');
        this.receiveCallback = receiveCallback;
    }
    async callFS(filepath, title, st, func, args, type, ae) {
        let _filepath = this.getPath(filepath);
        if (_filepath) {
            let content;
            try {
                content = await func.apply(this.fs, [_filepath, ...args]);
            } catch (e) {
                console.warn(e);
                content = '';
            }
            if (title) {
                let v = ae ? ae(content) : content;
                st.fsm.PostMessage(new roar.UserMessage(title, type, v), null);
            }
            if (this.receiveCallback) {
                this.receiveCallback();
            }
        } else {
            if (title) {
                let v = ae ? ae(null) : null;
                st.fsm.PostMessage(new roar.UserMessage(title, type, v), null);
            }
        }
    }
    install(script) {
        this.fs = new FS();
        script.InstallLib("simplefile", "simplefile", [
            // text
            script.NativeUtil.closureVoid(async (filepath, title, st) => {
                this.callFS(filepath, title, st, this.fs.readFile, [{ encoding: 'utf8' }], 'String', (v) => v == null ? '' : v);
            }, ['StringRegister', 'StringRegister'], true),

            script.NativeUtil.closureVoid(async (filepath, content, st) => {
                this.callFS(filepath, null, st, this.fs.writeFile, [content, { encoding: 'utf8' }]);
            }, ['StringRegister', 'StringRegister'], true),

            script.NativeUtil.closureVoid(async (filepath, content, st) => {
                this.callFS(filepath, null, st, this.fs.appendFile, [content, { encoding: 'utf8' }]);
            }, ['StringRegister', 'StringRegister'], true),
            // bin
            script.NativeUtil.closureVoid(async (filepath, title, st) => {
                this.callFS(filepath, title, st, this.fs.readFile, [], 'list<Integer>', (v) => new roar.OBList(v == null ? [] : v));
            }, ['StringRegister', 'StringRegister'], true),

            script.NativeUtil.closureVoid(async (filepath, content, st) => {
                this.callFS(filepath, null, st, this.fs.writeFile, [content.c]);
            }, ['StringRegister', 'StructRegister'], true),

            script.NativeUtil.closureVoid(async (filepath, content, st) => {
                this.callFS(filepath, null, st, this.fs.appendFile, [content.c]);
            }, ['StringRegister', 'StructRegister'], true),
            // del
            script.NativeUtil.closureVoid(async (filepath, content, st) => {
                this.callFS(filepath, null, st, this.fs.unlink, []);
            }, ['StringRegister'], true),
        ]);
    }
    getPath(filepath) {
        let subpath = path.join(this.basePath, filepath);
        let relative = path.relative(this.basePath, subpath);
        if (relative.startsWith('..')) {
            console.log('Wrong path', subpath);
            return null
        };
        return subpath;
    }
}
export default VRRFile;